# -*- coding: cp866 -*-

import MSConfig; c = MSConfig.config()
import MSLogger; log = MSLogger.get('wsgi_server')
import MSDatetime
import MSUsers
import MSObject
import ReaderMSAPI
import ReaderMSDB
import threading
import time
import smtplib
from email.mime.text import MIMEText
import pdb

def Database_threadUpdate(isTerminated, users, threadUpdateTimeout):
    if threadUpdateTimeout == 0: threadUpdateTimeout = 120

    log.info('Database::threadUpdate: started with timeout(%d)', threadUpdateTimeout)
    ctr = 0
    while not isTerminated.is_set():
        if ctr >= threadUpdateTimeout:
            try:
                users.load()
            except Exception, err:
                log.error('Database::threadUpdate: users update %s', str(err))
            ctr = 0
        time.sleep(2)
        ctr += 2
    log.info('Database::threadUpdate: stopped')

def Database_threadMonitor(isTerminated, users):
    log.info('Database::threadMonitor: started')
    while not isTerminated.is_set():
        for uid,user in users.objs.iteritems():
            if isTerminated.is_set(): 
                log.info('Database::threadMonitor: stopped')
                return
            reader = None
            if (not user.storage.meta.has_key('status')) or (user.storage.meta['status'] != 'A'): continue
            if user.storage.meta.has_key('skey'):
                reader = ReaderMSAPI.Reader(c.get('msapi','url'))
            if user.storage.meta.has_key('msId'):
                reader = ReaderMSDB.Reader(c.get('msdb','host'),
                                            c.get('msdb','port'),
                                            c.get('msdb','dbname'),
                                            c.get('msdb','user'),
                                            c.get('msdb','pwd'))

            startdate = MSDatetime.getLastHours(24*30*3)[0]

            if reader != None:
                obj = MSObject.MSObject(c.get('main', 'dbpath'), 'nonwriteable_object_thrMon')
                obj.open()
                if obj.storage.txn > 0:
                    raise RuntimeError('event_protocol: nonwriteable_object exists!')
                if user.storage.meta.has_key('skey'):
                    obj.storage.meta['skey'] = user.storage.meta['skey']
                if user.storage.meta.has_key('msId'):
                    obj.storage.meta['msId'] = user.storage.meta['msId']
                reader.getEvents_TIMED(obj, startdate)

                copied_events = obj.events
                copied_events.reverse()
                if len(copied_events) > 0:
                    last = copied_events[0]
                    now = MSDatetime.datetime.datetime.fromtimestamp(time.time())
                    alarmHours = 24
                    try:
                        if user.storage.meta.has_key('alarmHours'): alarmHours = int(user.storage.meta['alarmHours'])
                        if alarmHours == 0: alarmHours = 1
                    except Exception, err:
                        log.error('Database::threadMonitor: alarmHours access error: %s', str(err))
                    if now - last.eventGenerateTime > MSDatetime.datetime.timedelta(hours=alarmHours) and user.storage.meta.has_key('realmail'):
                        try:
                            fromaddr = 'monitor@gps.an-direct.ru'
                            toaddr = user.storage.meta['realmail']
                            msg = 'Потеряна связь с объектом ' + user.storage.meta['email'] + '( ' + user.storage.meta['realname'] + '): более 24х часов\n<br>'
                            msg+= 'Lost connection with object ' + user.storage.meta['email'] + '( ' + user.storage.meta['realname'] + '): more than 24 hours'
                            srv = '192.168.78.104'
                            server = smtplib.SMTP(srv)
                            server.sendmail(fromaddr, toaddr, msg)
                            server.quit()
                        except Exception, err:
                            log.error('can\'t send alarm mail for %s:%s to %s, smtp server is %s', user.storage.meta['email'], user.storage.meta['realname'], user.storage.meta['realmail'], srv)
        MSDatetime.time.sleep(600)
    log.info('Database::threadMonitor: stopped')
    return


class Database:
    def __init__(self, path):
        self.users = MSUsers.MSUsers(path)
        self.isTerminated = threading.Event(); self.isTerminated.clear()
        self.threadUpdate = None
        self.threadMonitor = None

    def setAutoUpdate(self, timeout):
        if timeout < 2: raise RuntimeError('wrong timeout value given')
        self.threadUpdate = threading.Thread(None, Database_threadUpdate, 'DatabaseUpdate', (self.isTerminated, self.users, timeout))
        self.threadUpdate.start()
        self.threadMonitor = threading.Thread(None, Database_threadMonitor, 'DatabaseMonitor', (self.isTerminated, self.users))
        self.threadMonitor.start()

    def start(self):
        log.debug('Database::start: ...')
        self.users.load()
        log.info('Database::start: done')

    def stop(self):
        log.debug('Database::stop: ...')
        self.isTerminated.set()
        if self.threadUpdate:   self.threadUpdate.join()
        if self.threadMonitor:  self.threadMonitor.join()
        log.info('Database::stop: done')

instance = None
def getInstance():
    global instance
    if instance == None: raise RuntimeError('Database instance is not created')
    return instance

##########################
if __name__ == "__main__":
    import sys
    if len(sys.argv) != 2: print "Usage: %s <storage_root>" % (sys.argv[0])
    g_root = sys.argv[1]

    def case1():
        o = MSObject.MSObject(g_root, 'test_obj')
        o.open()
        o.storage.destroy()

        db = Database(g_root)
        db.start()
        db.setAutoUpdate(10)

        try:
            o = MSObject.MSObject(g_root, 'test_obj')
            o.open()
            o.storage.meta['msId'] = 79
            o.append([1, 2, 3])
            o.flush()

            time.sleep(11)
            if db.objs.find('test_obj') == None: raise RuntimeError('case1: failed 1')
        finally:
            db.stop()

    case1()
